Fix numpad handling in QEMU's VNC server. The keymaps that we have include
authorEwan Mellor <ewan@xensource.com>
Mon, 4 Dec 2006 17:52:33 +0000 (17:52 +0000)
committerEwan Mellor <ewan@xensource.com>
Mon, 4 Dec 2006 17:52:33 +0000 (17:52 +0000)
information on which keys change depending upon the numlock setting, but
this isn't being used.  By forcing numlock on and off as necessary, when
receiving these keysyms through the VNC connection, we ensure that the
server's numlock status is the same as the client's.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
tools/ioemu/keymaps.c
tools/ioemu/vnc.c
tools/ioemu/vnc_keysym.h

index bd893288aa0cc5e6936aaa7e5b09ff6d48a78b81..e7dd4a8b1152b047d62eeeba262a433319c3a761 100644 (file)
@@ -36,8 +36,10 @@ static int get_keysym(const char *name)
 #define MAX_EXTRA_COUNT 256
 typedef struct {
     uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
+    int keysym2numlock[MAX_NORMAL_KEYCODE];
     struct {
        int keysym;
+       int numlock;
        uint16_t keycode;
     } keysym2keycode_extra[MAX_EXTRA_COUNT];
     int extra_count;
@@ -50,6 +52,8 @@ static kbd_layout_t *parse_keyboard_layout(const char *language,
     char file_name[1024];
     char line[1024];
     int len;
+    int *keycode2numlock;
+    int i;
 
     snprintf(file_name, sizeof(file_name),
              "%s/keymaps/%s", bios_dir, language);
@@ -63,6 +67,15 @@ static kbd_layout_t *parse_keyboard_layout(const char *language,
                "Could not read keymap file: '%s'\n", file_name);
        return 0;
     }
+
+    /* Allocate a temporary map tracking which keycodes change when numlock is
+       set.  Keycodes are 16 bit, so 65536 is safe. */
+    keycode2numlock = malloc(65536 * sizeof(int));
+    if (!keycode2numlock) {
+        perror("Could not read keymap file");
+       return 0;
+    }
+
     for(;;) {
        if (fgets(line, 1024, f) == NULL)
             break;
@@ -86,13 +99,19 @@ static kbd_layout_t *parse_keyboard_layout(const char *language,
                if (keysym == 0) {
                     //             fprintf(stderr, "Warning: unknown keysym %s\n", line);
                } else {
-                   const char *rest = end_of_keysym + 1;
-                   int keycode = strtol(rest, NULL, 0);
+                   char *rest = end_of_keysym + 1;
+                   int keycode = strtol(rest, &rest, 0);
+                   int numlock = (rest != NULL &&
+                                  strstr(rest, "numlock") != NULL);
+
+                    keycode2numlock[keycode] = numlock;
+
                    /* if(keycode&0x80)
                       keycode=(keycode<<8)^0x80e0; */
                    if (keysym < MAX_NORMAL_KEYCODE) {
                        //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode);
                        k->keysym2keycode[keysym] = keycode;
+                       k->keysym2numlock[keysym] = numlock;
                    } else {
                        if (k->extra_count >= MAX_EXTRA_COUNT) {
                            fprintf(stderr,
@@ -107,6 +126,8 @@ static kbd_layout_t *parse_keyboard_layout(const char *language,
                                keysym = keysym;
                            k->keysym2keycode_extra[k->extra_count].
                                keycode = keycode;
+                           k->keysym2keycode_extra[k->extra_count].
+                               numlock = numlock;
                            k->extra_count++;
                        }
                    }
@@ -115,6 +136,22 @@ static kbd_layout_t *parse_keyboard_layout(const char *language,
        }
     }
     fclose(f);
+
+    for (int i = 0; i < MAX_NORMAL_KEYCODE; i++) {
+        if (k->keysym2numlock[i] != 1) {
+            k->keysym2numlock[i] = -keycode2numlock[k->keysym2keycode[i]];
+        }
+    }
+
+    for (int i = 0; i < k->extra_count; i++) {
+        if (k->keysym2keycode_extra[i].numlock != 1) {
+            k->keysym2keycode_extra[i].numlock =
+                -keycode2numlock[k->keysym2keycode_extra[i].keycode];
+        }
+    }
+
+    free(keycode2numlock);
+
     return k;
 }
 
@@ -143,3 +180,25 @@ static int keysym2scancode(void *kbd_layout, int keysym)
     }
     return 0;
 }
+
+/**
+ * Returns 1 if the given keysym requires numlock to be pressed, -1 if it
+ * requires it to be cleared, and 0 otherwise.
+ */
+static int keysym2numlock(void *kbd_layout, int keysym)
+{
+    kbd_layout_t *k = kbd_layout;
+    if (keysym < MAX_NORMAL_KEYCODE) {
+       return k->keysym2numlock[keysym];
+    } else {
+       int i;
+#ifdef XK_ISO_Left_Tab
+       if (keysym == XK_ISO_Left_Tab)
+           keysym = XK_Tab;
+#endif
+       for (i = 0; i < k->extra_count; i++)
+           if (k->keysym2keycode_extra[i].keysym == keysym)
+               return k->keysym2keycode_extra[i].numlock;
+    }
+    return 0;
+}
index 86d3720f903c6fabf8b995505b3ec028167cc551..6d7cc31d4ae99bce98e29f42e8b8b6c58933a086 100644 (file)
@@ -115,6 +115,7 @@ struct VncState
 
     int ctl_keys;               /* Ctrl+Alt starts calibration */
     int shift_keys;             /* Shift / CapsLock keys */
+    int numlock;
 };
 
 #define DIRTY_PIXEL_BITS 64
@@ -854,14 +855,40 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
     }
 }
 
+static void press_key(VncState *vs, int keycode)
+{
+    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) & 0x7f);
+    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) | 0x80);
+}
+
 static void do_key_event(VncState *vs, int down, uint32_t sym)
 {
     sym &= 0xFFFF;
 
     if (is_graphic_console()) {
        int keycode;
+       int numlock;
 
        keycode = keysym2scancode(vs->kbd_layout, sym);
+       numlock = keysym2numlock(vs->kbd_layout, sym);
+
+        /* If the numlock state needs to change then simulate an additional
+           keypress before sending this one.  This will happen if the user
+           toggles numlock away from the VNC window.
+        */
+       if (numlock == 1) {
+           if (!vs->numlock) {
+               vs->numlock = 1;
+               press_key(vs, XK_Num_Lock);
+           }
+       }
+       else if (numlock == -1) {
+           if (vs->numlock) {
+               vs->numlock = 0;
+               press_key(vs, XK_Num_Lock);
+           }
+        }
+
        if (keycode & 0x80)
            kbd_put_keycode(0xe0);
        if (down)
@@ -932,6 +959,10 @@ static void do_key_event(VncState *vs, int down, uint32_t sym)
            vs->shift_keys ^= 2;
            break;
 
+       case XK_Num_Lock:
+           vs->numlock = !vs->numlock;
+           break;
+
        case XK_1 ... XK_9:
            if ((vs->ctl_keys & 3) != 3)
                break;
@@ -1355,6 +1386,7 @@ int vnc_display_init(DisplayState *ds, int display, int find_unused, struct sock
     vs->lsock = -1;
     vs->csock = -1;
     vs->depth = 4;
+    vs->numlock = 0;
 
     vs->ds = ds;
 
index 14fe47f9e1cdd3805358363cdc271df98c10a225..5c471044426a2c45bb2b224794a96319183b2a91 100644 (file)
@@ -231,6 +231,19 @@ static name2keysym_t name2keysym[]={
 {"Home", 0xff50},      /* XK_Home */
 {"End", 0xff57},       /* XK_End */
 {"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */
+{"KP_Home", 0xff95},
+{"KP_Left", 0xff96},
+{"KP_Up", 0xff97},
+{"KP_Right", 0xff98},
+{"KP_Down", 0xff99},
+{"KP_Prior", 0xff9a},
+{"KP_Page_Up", 0xff9a},
+{"KP_Next", 0xff9b},
+{"KP_Page_Down", 0xff9b},
+{"KP_End", 0xff9c},
+{"KP_Begin", 0xff9d},
+{"KP_Insert", 0xff9e},
+{"KP_Delete", 0xff9f},
 {"F1", 0xffbe},        /* XK_F1 */
 {"F2", 0xffbf},        /* XK_F2 */
 {"F3", 0xffc0},        /* XK_F3 */
@@ -258,6 +271,7 @@ static name2keysym_t name2keysym[]={
 {"KP_8", 0xffb8},      /* XK_KP_8 */
 {"KP_9", 0xffb9},      /* XK_KP_9 */
 {"KP_Add", 0xffab},    /* XK_KP_Add */
+{"KP_Separator", 0xffac},/* XK_KP_Separator */
 {"KP_Decimal", 0xffae},  /* XK_KP_Decimal */
 {"KP_Divide", 0xffaf},   /* XK_KP_Divide */
 {"KP_Enter", 0xff8d},    /* XK_KP_Enter */